package com.shelpe.services.keywords.controller;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.shelpe.services.keywords.entity.jpa.Adgroup;
import com.shelpe.services.keywords.entity.jpa.Campaign;
import com.shelpe.services.keywords.entity.jpa.KeywordsLog;
import com.shelpe.services.keywords.entity.jpa.OnlineStatus;
import com.shelpe.services.keywords.model.Keyword;
import com.shelpe.services.keywords.model.KeywordExample;
import com.shelpe.services.keywords.model.RptKeyword;
import com.shelpe.services.keywords.model.RptKeywordExample;
import com.shelpe.services.keywords.model.RptKeywordParam;
import com.shelpe.services.keywords.readdao.KeywordMapper;
import com.shelpe.services.keywords.readdao.ReadRptKeywordMapper;
import com.shelpe.services.keywords.repository.jpa.AdgroupRepository;
import com.shelpe.services.keywords.repository.jpa.CampaignRepository;
import com.shelpe.services.keywords.repository.jpa.KeywordsLogRepository;
import com.shelpe.services.keywords.service.RptKeywordService;
import com.shelpe.services.keywords.service.impl.OopKeywordService;
import com.shelpe.services.keywords.service.impl.OopKeywordService.SearchSettings;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping("/keywordsservice")
@Api(value = "关键词相关的API", consumes = "application/json", produces = "application/json")
public class KeywordsController {

	@Autowired
	private OopKeywordService keywordService;
	@Autowired
	private ReadRptKeywordMapper rptKeywordMapper;
	@Autowired
	private KeywordsLogRepository keywordsLogRepository;
	@Autowired
	private AdgroupRepository adgroupRepository;
	@Autowired
	private CampaignRepository campaignRepository;
	@Autowired
	private RptKeywordService rptKeywordService;
	@Autowired
	private KeywordMapper keywordMapper;

	@ApiOperation(value = "根据所给的推广计划编号或者推广组编号查找其关键词以及效果数据")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "device", defaultValue = "pc", allowableValues = "pc, mobile", paramType = "query", dataType = "string") })
	@RequestMapping(path = "/users/{user_id}/keywords/@semiauto", method = RequestMethod.GET)
	@ResponseBody
	public List<KeywordDto> find2Keywords(@PathVariable("user_id") Long userID,
			@RequestParam(required = false, name = "adgroup_ids") List<Long> adgroupIDs,
			@RequestParam(required = false, name = "campaign_ids") List<Long> campaignIDs,
			@RequestParam(name = "device", defaultValue = "pc") String device,
			@RequestParam(name = "days", defaultValue = "7") int days) {
		return this.find2Keywords(userID, adgroupIDs, campaignIDs, device, days, null);
	}

	@ApiOperation(value = "根据所给的推广计划编号或者推广组编号查找其关键词以及效果数据")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "device", defaultValue = "pc", allowableValues = "pc, mobile", paramType = "query", dataType = "string") })
	@RequestMapping(path = "/users/{user_id}/keywords/@semiauto", method = RequestMethod.POST)
	@ResponseBody
	public List<KeywordDto> findKeywordsWithCriterias(@PathVariable("user_id") Long userID,
			@RequestParam(required = false, name = "adgroup_ids") List<Long> adgroupIDs,
			@RequestParam(required = false, name = "campaign_ids") List<Long> campaignIDs,
			@RequestParam(name = "device", defaultValue = "pc") String device,
			@RequestParam(name = "days", defaultValue = "7") int days,
			@RequestBody(required = false) List<SearchCriteria> criterias) {
		return this.find2Keywords(userID, adgroupIDs, campaignIDs, device, days, criterias);
	}

	public List<KeywordDto> find2Keywords(long userID, List<Long> adgroupIDs, List<Long> campaignIDs, String device,
			int days, List<SearchCriteria> criterias) {
		long start = System.currentTimeMillis();
		List<KeywordDto> rets = new ArrayList<KeywordDto>();

		if ((adgroupIDs == null || adgroupIDs.size() == 0) && (campaignIDs == null || campaignIDs.size() == 0)) {
			return rets;
		}

		SearchSettings ss = new SearchSettings();
		ss.setOnlineAdgroup(true);
		ss.setCriterias(criterias);
		List<EffectableKeywordDto> keywords = this.keywordService.findKeywords(userID, adgroupIDs, campaignIDs, device,
				days, ss);
		if (keywords.size() == 0) {
			return rets;
		}
		List<Long> keywordIDs = new ArrayList<Long>();
		List<Long> foundAdgroupIDs = new ArrayList<Long>();
		for (EffectableKeywordDto keyword : keywords) {
			keywordIDs.add(keyword.getKeywordID());
			foundAdgroupIDs.add(keyword.getAdgroupID());
			KeywordDto dto = new KeywordDto();
			BeanUtils.copyProperties(keyword, dto);
			rets.add(dto);
		}

		// 是否运行过
		Date today = DateUtils.truncate(new Date(), Calendar.DATE);
		List<KeywordsLog> logs = this.keywordsLogRepository.findByKeywordIDInAndUpdatedTimeAfterAndAction(keywordIDs,
				today, KeywordsLog.Action.SEMIAUTO_EXEC_UPDATE);
		for (KeywordDto dto : rets) {
			if (this.checkFound(logs, dto.getKeywordID())) {
				dto.executed = true;
				log.debug("{} is executed", dto);
			}
		}

		// 是否有收益
		List<RptKeyword> adgroupEffects = this.getAvgCtr(userID, foundAdgroupIDs, days, device);
		for (KeywordDto dto : rets) {
			RptKeyword rpt = this.filterByAdgroupID(adgroupEffects, dto.getAdgroupID());
			float avg = rpt != null ? rpt.getCtr() : 0;
			if (dto.getRoi() > 0 && dto.getClickRatio() > (1.2 * avg)) {
				dto.earning = true;
				log.debug("{} has earning", dto);
			} else {
				dto.earning = false;
			}
		}

		log.info("find2Keywords find {} keywords cost {} ms: campaignIDs({}) - adgroupIDs({})", rets.size(),
				System.currentTimeMillis() - start, campaignIDs, adgroupIDs);
		return rets;
	}

	private boolean checkFound(List<KeywordsLog> logs, long keywordID) {
		for (KeywordsLog kLog : logs) {
			if (kLog.getKeywordID() == keywordID) {
				return true;
			}
		}
		return false;
	}

	private List<RptKeyword> getAvgCtr(long userID, List<Long> adgroupIDs, int days, String device) {
		RptKeywordExample example = new RptKeywordExample();
		example.setUserId(userID);
		RptKeywordExample.Criteria criteria = this.buildRptKeywordExampleCriteria(example, days, device);
		criteria.andAdgroupIdIn(adgroupIDs);
		return this.rptKeywordMapper.selectReportsTrendByCampaignId(example);
	}

	private RptKeyword filterByAdgroupID(List<RptKeyword> rpts, long adgroupID) {
		for (RptKeyword rpt : rpts) {
			if (rpt.getAdgroupId() == adgroupID) {
				return rpt;
			}
		}
		return null;
	}

	private RptKeywordExample.Criteria buildRptKeywordExampleCriteria(RptKeywordExample example, int days,
			String device) {
		RptKeywordExample.Criteria criteria = example.createCriteria();
		Date yesterday = DateUtils.addDays(DateUtils.truncate(new Date(), Calendar.DATE), -1);
		criteria.andDateBetween(DateUtils.addDays(yesterday, 1 - days), yesterday);

		if ("mobile".equals(device)) {
			criteria.andSourceBetween((byte) 4, (byte) 5);
		} else {
			criteria.andSourceBetween((byte) 1, (byte) 2);
		}
		return criteria;
	}

	@Data
	public static class KeywordsDto {
		@JsonProperty("adgroup_id")
		private long adgroupID;
		private List<KeywordDto> words;
		private Adgroup adgroup;
	}

	@Data
	@EqualsAndHashCode(callSuper = false)
	@ToString(callSuper = true)
	public static class KeywordDto extends EffectableKeywordDto {
		@JsonProperty("earning")
		private boolean earning;
		@JsonProperty("executed")
		private boolean executed;
	}

	/**
	 * 删除垃圾词 - 查询符合条件的垃圾词
	 * 
	 * @param userId
	 *            用户ID
	 * @param campaignIds
	 *            推广计划ids
	 * @param adgroupIds
	 *            推广组ids
	 * @param clickDay
	 *            查询点击量的天数
	 * @param impressionsDay
	 *            查询展现量的天数
	 * @param qscore
	 *            PC质量得分
	 * @param mobileQscore
	 *            无线质量得分
	 * @return
	 */
	@RequestMapping(method = RequestMethod.GET, value = "/spamwords")
	public @ResponseBody SpamKeywordsDto spamwords(@RequestParam(required = false) Long userId,
			@RequestParam(required = false) List<Long> campaignIds,
			@RequestParam(required = false) List<Long> adgroupIds, @RequestParam(required = false) Integer clickDay,
			@RequestParam(required = false) Integer impressionsDay, @RequestParam(required = false) Integer qscore,
			@RequestParam(required = false) Integer mobileQscore) {
		long start = System.currentTimeMillis();

		if (userId == null) {
			if (adgroupIds != null && adgroupIds.size() > 0) {
				Adgroup adgroup = adgroupRepository.findOne(adgroupIds.get(0));
				userId = adgroup.getUserID();
			} else if (campaignIds != null && campaignIds.size() > 0) {
				Campaign campaign = this.campaignRepository.findOne(campaignIds.get(0));
				userId = campaign.getUserID();
			}

			if (userId == null) {
				throw new IllegalArgumentException("userID_not_found");
			}
		}

		RptKeywordParam param = new RptKeywordParam();
		param.setUserId(userId);
		if (adgroupIds != null && adgroupIds.size() > 0) {
			param.setAdgroupIds(adgroupIds);
		}
		if (campaignIds != null && campaignIds.size() > 0) {
			param.setCampaignIds(campaignIds);
		}
		if (clickDay != null) {
			param.setClickDay(clickDay);
		}
		if (impressionsDay != null) {
			param.setImpressionsDay(impressionsDay);
		}
		if (qscore != null) {
			param.setQscore(qscore);
		}
		if (mobileQscore != null) {
			param.setMobileQscore(mobileQscore);
		}

		List<RptKeywordService.KeywordCompositeID> ids = rptKeywordService.getKeywordIds(param);
		SpamKeywordsDto dto = new SpamKeywordsDto();
		dto.setUserID(userId);
		if (ids != null && ids.size() > 0) {
			// 查看在keyword表中是否存在
			List<Long> foundKeywordIDs = new ArrayList<Long>();
			for (RptKeywordService.KeywordCompositeID id : ids) {
				foundKeywordIDs.add(id.getKeywordID());
			}
			KeywordExample ke = new KeywordExample();
			ke.setUserId(userId);
			ke.createCriteria().andKeywordIdIn(foundKeywordIDs);
			List<Keyword> foundKeywords = this.keywordMapper.selectByExample(ke);
			// 查看对应的adgroup是否存在
			List<Long> adgroupIDs = new ArrayList<Long>();
			Date date = new Date();
			for (Keyword kw : foundKeywords) {
				// 过滤三天内添加的
				if(!this.filterSpecialUser(userId)){
					Date ago3 = DateUtils.addDays(DateUtils.truncate(kw.getCreateTime(), Calendar.DATE), 3);
					if (date.before(ago3)) {
						continue;
					}
				}
				adgroupIDs.add(kw.getAdgroupId());
			}
			List<Adgroup> adgroups = this.adgroupRepository.findByAutoIDIn(adgroupIDs);
			adgroupIDs = new ArrayList<Long>();
			for (Adgroup adgroup : adgroups) {
				if (adgroup.getOnlineStatus() != OnlineStatus.offline) {
					adgroupIDs.add(adgroup.getAutoID());
				}
			}

			// 最终有效的keyword_id
			foundKeywordIDs = new ArrayList<Long>();
			for (Keyword kw : foundKeywords) {
				if (adgroupIDs.indexOf(kw.getAdgroupId()) > -1) {
					foundKeywordIDs.add(kw.getKeywordId());
				}
			}

			dto.setCount(foundKeywordIDs.size());
			Map<Long, CampaignSpamKeywords> map = new HashMap<Long, CampaignSpamKeywords>();
			for (RptKeywordService.KeywordCompositeID id : ids) {
				if (foundKeywordIDs.contains(id.getKeywordID())) {
					CampaignSpamKeywords csk = map.get(id.getCampaignID());
					if (csk == null) {
						csk = new CampaignSpamKeywords();
						csk.setCampaignID(id.getCampaignID());
						map.put(id.getCampaignID(), csk);
					}
					csk.addKeywordID(id.getKeywordID());
				}
			}
			dto.setKeywords(new ArrayList<CampaignSpamKeywords>());
			for (CampaignSpamKeywords csk : map.values()) {
				dto.getKeywords().add(csk);
			}
		}

		log.info("select spamwords cost {} ms", System.currentTimeMillis() - start, param);
		return dto;
	}

	private boolean filterSpecialUser(long userID) {
		List<Long> userIDs = new ArrayList<Long>();
		userIDs.add(619123122L);
		userIDs.add(277227205L);
		if (userIDs.indexOf(userID) > -1) {
			return true;
		}
		return false;
	}
	
	@Data
	@JsonInclude(Include.NON_NULL)
	public static class SpamKeywordsDto {
		private int count;
		@JsonProperty("user_id")
		private long userID;
		private List<CampaignSpamKeywords> keywords;
	}

	@Data
	private static class CampaignSpamKeywords {
		@JsonProperty("campaign_id")
		private long campaignID;
		@JsonProperty("keyword_ids")
		private List<Long> keywordIDs;

		public void addKeywordID(long keywordID) {
			if (this.keywordIDs == null) {
				this.keywordIDs = new ArrayList<Long>();
			}
			this.keywordIDs.add(keywordID);
		}
	}
}
